package org.smoozik.app.common.piano;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.util.ArrayList;

import javax.swing.JPanel;

import org.smoozik.services.musictheory.Figure;
import org.smoozik.services.musictheory.Note;

@SuppressWarnings("serial")
public class PianoPanel extends JPanel {

	// //////////////////////////////////
	// Graphical constants
	Color pianoColor = Color.DARK_GRAY;
	Color pianoBgColor = Color.WHITE;
	Color whiteKeyColor = Color.WHITE;
	Color blackKeyColor = Color.DARK_GRAY;
	boolean whiteKeyFill = false;
	boolean blackKeyFill = true;
	int whiteKeyWidth = 16;
	int whiteKeyHeight = 70;
	int blackKeyHeight = 40;
	int m0 = 150;
	int h0 = 1;
	int blackKeyWidth = whiteKeyWidth / 2;
	int blackKeyPad = whiteKeyWidth / 4;

	// //////////////////////////////////
	// Piano structure
	int nbKeys = 88;
	int pianoWidth;
	int keyC0 = 23;
	ArrayList<PianoKey> blackKeys;
	ArrayList<PianoKey> whiteKeys;

	// //////////////////////////////////
	// Action related elements
	PianoClient pianoClient;
	private Figure currentFigure;
	private Note currentNote;

	public PianoPanel(PianoClient pianoClient) {
		super();
		// this.addMouseListener(this);
		// this.addMouseMotionListener(this);
		this.pianoClient = pianoClient;
		buildKeys();
		setDefaultListener();
	}

	public PianoPanel() {
		this(null);
	}

	public void setDefaultListener() {
		PianoListener pl = new PianoListener(this);
		this.addMouseListener(pl);
		this.addMouseMotionListener(pl);
	}

	//
	// public methods
	//

	public Note getCurrentNote() {
		return currentNote;
	}

	public void setCurrentNote(Note currentNote) {
		this.currentNote = currentNote;
	}

	public Figure getCurrentFigure() {
		return currentFigure;
	}

	public void setCurrentFigure(Figure figure) {
		// replace the previous figure
		this.currentFigure = figure;
		unlightKeys();

		if (figure == null) {
			return;
		}

		if (figure.getNotes() != null) {
			for (Note note : figure.getNotes()) {
				SetPianoKeyLit(note);
			}
		}
	}

	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		// white keys display
		for (PianoKey key : this.whiteKeys) {
			key.paint(g);
		}

		// black keys display
		for (PianoKey key : this.blackKeys) {
			key.paint(g);
		}
	}

	//
	// private methods
	//

	// //////////////////////////////////
	// piano building

	private void buildKeys() {
		// key attributes
		int x = this.m0;
		int y = this.h0;
		int noteDiff;

		// starting note
		Note note = new Note(-keyC0);

		this.blackKeys = new ArrayList<>();
		this.whiteKeys = new ArrayList<>();

		for (int i = 0; i < this.nbKeys; i++) {
			if (!note.isSharpNote()) {
				note.setIndexToHauteur();
				buildWhiteKey((Note) note.clone(), x, y);
				x += whiteKeyWidth;
			} else {
				// the last key cannot be a black key
				if (i != this.nbKeys - 1) {
					buildBlackKey((Note) note.clone(), x, y);
				}
			}
			note.sharp();
			noteDiff = note.getDiff();
			if (noteDiff > 1) {
				note.setIndexNote(note.getIndexNote() + 1);
			}
		}
	}

	private void buildBlackKey(Note note, int x, int y) {
		PianoKey key = new PianoKey(note, x - blackKeyPad, y, blackKeyWidth,
				blackKeyHeight, blackKeyColor);

		this.blackKeys.add(key);
	}

	private void buildWhiteKey(Note note, int x, int y) {
		PianoKey key = new PianoKey(note, x, y, whiteKeyWidth, whiteKeyHeight,
				whiteKeyColor);

		this.whiteKeys.add(key);
	}

	// public void transpose(Interval interval) {
	// for (PianoKey key : getAllKeys()) {
	// key.getNote().addIntervalle(interval);
	// }
	// }

	private ArrayList<PianoKey> getAllKeys() {
		ArrayList<PianoKey> list = new ArrayList<>();

		if (this.blackKeys != null) {
			list.addAll(this.blackKeys);
		}
		if (this.whiteKeys != null) {
			list.addAll(this.whiteKeys);
		}

		return list;
	}

	private void SetPianoKeyLit(Note note) {
		for (PianoKey key : getAllKeys()) {
			if (key.getNote().equalsIgnoreSharp(note)) {
				key.lightKey();
			}
		}
	}

	private void unlightKeys() {
		for (PianoKey key : getAllKeys()) {
			key.unlightKey();
		}
	}

	public PianoKey getFigureForMouse(MouseEvent e) {
		int x = e.getX();
		int y = e.getY();
		PianoKey key = getNoteForPoint(x, y);

		return key;
	}

	private PianoKey getNoteForPoint(int x, int y) {
		for (PianoKey key : getAllKeys()) {
			if (key.contains(x, y)) {
				return key;
			}
		}

		return null;
	}

	public void updateKeyPressed(PianoKey key) {
		if (key == null) {
			return;
		}

		Note note = key.getNote();

		if (note != null && !note.equalsIgnoreSharp(currentNote)) {
			this.currentNote = note;
			setCurrentFigure(note);
			if (this.pianoClient != null) {
				this.pianoClient.keyPressed(note);
			}
		}
	}

	public PianoClient getPianoClient() {
		return pianoClient;
	}

	public void setPianoClient(PianoClient pianoClient) {
		this.pianoClient = pianoClient;
	}

}
